home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tcl7.4 / tclEnv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-04  |  13.2 KB  |  518 lines

  1. /* 
  2.  * tclEnv.c --
  3.  *
  4.  *    Tcl support for environment variables, including a setenv
  5.  *    procedure.
  6.  *
  7.  * Copyright (c) 1991-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. #ifndef lint
  15. static char sccsid[] = "@(#) tclEnv.c 1.21 95/01/03 17:05:31";
  16. #endif /* not lint */
  17.  
  18. /*
  19.  * The putenv and setenv definitions below cause any system prototypes for
  20.  * those procedures to be ignored so that there won't be a clash when the
  21.  * versions in this file are compiled.
  22.  */
  23.  
  24. #define putenv ignore_putenv
  25. #define setenv ignore_setenv
  26. #include "tclInt.h"
  27. #include "tclPort.h"
  28. #undef putenv
  29. #undef setenv
  30.  
  31. /*
  32.  * The structure below is used to keep track of all of the interpereters
  33.  * for which we're managing the "env" array.  It's needed so that they
  34.  * can all be updated whenever an environment variable is changed
  35.  * anywhere.
  36.  */
  37.  
  38. typedef struct EnvInterp {
  39.     Tcl_Interp *interp;        /* Interpreter for which we're managing
  40.                  * the env array. */
  41.     struct EnvInterp *nextPtr;    /* Next in list of all such interpreters,
  42.                  * or zero. */
  43. } EnvInterp;
  44.  
  45. static EnvInterp *firstInterpPtr;
  46.                 /* First in list of all managed interpreters,
  47.                  * or NULL if none. */
  48.  
  49. static int environSize = 0;    /* Non-zero means that the all of the
  50.                  * environ-related information is malloc-ed
  51.                  * and the environ array itself has this
  52.                  * many total entries allocated to it (not
  53.                  * all may be in use at once).  Zero means
  54.                  * that the environment array is in its
  55.                  * original static state. */
  56.  
  57. /*
  58.  * Declarations for local procedures defined in this file:
  59.  */
  60.  
  61. static void        EnvInit _ANSI_ARGS_((void));
  62. static char *        EnvTraceProc _ANSI_ARGS_((ClientData clientData,
  63.                 Tcl_Interp *interp, char *name1, char *name2,
  64.                 int flags));
  65. static int        FindVariable _ANSI_ARGS_((CONST char *name,
  66.                 int *lengthPtr));
  67. void            TclSetEnv _ANSI_ARGS_((CONST char *name,
  68.                 CONST char *value));
  69. void            TclUnsetEnv _ANSI_ARGS_((CONST char *name));
  70.  
  71. /*
  72.  *----------------------------------------------------------------------
  73.  *
  74.  * TclSetupEnv --
  75.  *
  76.  *    This procedure is invoked for an interpreter to make environment
  77.  *    variables accessible from that interpreter via the "env"
  78.  *    associative array.
  79.  *
  80.  * Results:
  81.  *    None.
  82.  *
  83.  * Side effects:
  84.  *    The interpreter is added to a list of interpreters managed
  85.  *    by us, so that its view of envariables can be kept consistent
  86.  *    with the view in other interpreters.  If this is the first
  87.  *    call to Tcl_SetupEnv, then additional initialization happens,
  88.  *    such as copying the environment to dynamically-allocated space
  89.  *    for ease of management.
  90.  *
  91.  *----------------------------------------------------------------------
  92.  */
  93.  
  94. void
  95. TclSetupEnv(interp)
  96.     Tcl_Interp *interp;        /* Interpreter whose "env" array is to be
  97.                  * managed. */
  98. {
  99.     EnvInterp *eiPtr;
  100.     int i;
  101.  
  102.     /*
  103.      * First, initialize our environment-related information, if
  104.      * necessary.
  105.      */
  106.  
  107.     if (environSize == 0) {
  108.     EnvInit();
  109.     }
  110.  
  111.     /*
  112.      * Next, add the interpreter to the list of those that we manage.
  113.      */
  114.  
  115.     eiPtr = (EnvInterp *) ckalloc(sizeof(EnvInterp));
  116.     eiPtr->interp = interp;
  117.     eiPtr->nextPtr = firstInterpPtr;
  118.     firstInterpPtr = eiPtr;
  119.  
  120.     /*
  121.      * Store the environment variable values into the interpreter's
  122.      * "env" array, and arrange for us to be notified on future
  123.      * writes and unsets to that array.
  124.      */
  125.  
  126.     (void) Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY);
  127.     for (i = 0; ; i++) {
  128.     char *p, *p2;
  129.  
  130.     p = environ[i];
  131.     if (p == NULL) {
  132.         break;
  133.     }
  134.     for (p2 = p; *p2 != '='; p2++) {
  135.         /* Empty loop body. */
  136.     }
  137.     *p2 = 0;
  138.     (void) Tcl_SetVar2(interp, "env", p, p2+1, TCL_GLOBAL_ONLY);
  139.     *p2 = '=';
  140.     }
  141.     Tcl_TraceVar2(interp, "env", (char *) NULL,
  142.         TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
  143.         EnvTraceProc, (ClientData) NULL);
  144. }
  145.  
  146. /*
  147.  *----------------------------------------------------------------------
  148.  *
  149.  * FindVariable --
  150.  *
  151.  *    Locate the entry in environ for a given name.
  152.  *
  153.  * Results:
  154.  *    The return value is the index in environ of an entry with the
  155.  *    name "name", or -1 if there is no such entry.   The integer at
  156.  *    *lengthPtr is filled in with the length of name (if a matching
  157.  *    entry is found) or the length of the environ array (if no matching
  158.  *    entry is found).
  159.  *
  160.  * Side effects:
  161.  *    None.
  162.  *
  163.  *----------------------------------------------------------------------
  164.  */
  165.  
  166. static int
  167. FindVariable(name, lengthPtr)
  168.     CONST char *name;        /* Name of desired environment variable. */
  169.     int *lengthPtr;        /* Used to return length of name (for
  170.                  * successful searches) or number of non-NULL
  171.                  * entries in environ (for unsuccessful
  172.                  * searches). */
  173. {
  174.     int i;
  175.     register CONST char *p1, *p2;
  176.  
  177.     for (i = 0, p1 = environ[i]; p1 != NULL; i++, p1 = environ[i]) {
  178.     for (p2 = name; *p2 == *p1; p1++, p2++) {
  179.         /* NULL loop body. */
  180.     }
  181.     if ((*p1 == '=') && (*p2 == '\0')) {
  182.         *lengthPtr = p2-name;
  183.         return i;
  184.     }
  185.     }
  186.     *lengthPtr = i;
  187.     return -1;
  188. }
  189.  
  190. /*
  191.  *----------------------------------------------------------------------
  192.  *
  193.  * TclSetEnv --
  194.  *
  195.  *    Set an environment variable, replacing an existing value
  196.  *    or creating a new variable if there doesn't exist a variable
  197.  *    by the given name.  This procedure is intended to be a
  198.  *    stand-in for the  UNIX "setenv" procedure so that applications
  199.  *    using that procedure will interface properly to Tcl.  To make
  200.  *    it a stand-in, the Makefile must define "TclSetEnv" to "setenv".
  201.  *
  202.  * Results:
  203.  *    None.
  204.  *
  205.  * Side effects:
  206.  *    The environ array gets updated, as do all of the interpreters
  207.  *    that we manage.
  208.  *
  209.  *----------------------------------------------------------------------
  210.  */
  211.  
  212. void
  213. TclSetEnv(name, value)
  214.     CONST char *name;        /* Name of variable whose value is to be
  215.                  * set. */
  216.     CONST char *value;        /* New value for variable. */
  217. {
  218.     int index, length, nameLength;
  219.     char *p;
  220.     EnvInterp *eiPtr;
  221.  
  222.     if (environSize == 0) {
  223.     EnvInit();
  224.     }
  225.  
  226.     /*
  227.      * Figure out where the entry is going to go.  If the name doesn't
  228.      * already exist, enlarge the array if necessary to make room.  If
  229.      * the name exists, free its old entry.
  230.      */
  231.  
  232.     index = FindVariable(name, &length);
  233.     if (index == -1) {
  234.     if ((length+2) > environSize) {
  235.         char **newEnviron;
  236.  
  237.         newEnviron = (char **) ckalloc((unsigned)
  238.             ((length+5) * sizeof(char *)));
  239.         memcpy((VOID *) newEnviron, (VOID *) environ,
  240.             length*sizeof(char *));
  241.         ckfree((char *) environ);
  242.         environ = newEnviron;
  243.         environSize = length+5;
  244.     }
  245.     index = length;
  246.     environ[index+1] = NULL;
  247.     nameLength = strlen(name);
  248.     } else {
  249.     /*
  250.      * Compare the new value to the existing value.  If they're
  251.      * the same then quit immediately (e.g. don't rewrite the
  252.      * value or propagate it to other interpreters).  Otherwise,
  253.      * when there are N interpreters there will be N! propagations
  254.      * of the same value among the interpreters.
  255.      */
  256.  
  257.     if (strcmp(value, environ[index]+length+1) == 0) {
  258.         return;
  259.     }
  260.     ckfree(environ[index]);
  261.     nameLength = length;
  262.     }
  263.  
  264.     /*
  265.      * Create a new entry and enter it into the table.
  266.      */
  267.  
  268.     p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
  269.     environ[index] = p;
  270.     strcpy(p, name);
  271.     p += nameLength;
  272.     *p = '=';
  273.     strcpy(p+1, value);
  274.  
  275.     /*
  276.      * Update all of the interpreters.
  277.      */
  278.  
  279.     for (eiPtr= firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) {
  280.     (void) Tcl_SetVar2(eiPtr->interp, "env", (char *) name,
  281.         p+1, TCL_GLOBAL_ONLY);
  282.     }
  283. }
  284.  
  285. /*
  286.  *----------------------------------------------------------------------
  287.  *
  288.  * Tcl_PutEnv --
  289.  *
  290.  *    Set an environment variable.  Similar to setenv except that
  291.  *    the information is passed in a single string of the form
  292.  *    NAME=value, rather than as separate name strings.  This procedure
  293.  *    is intended to be a stand-in for the  UNIX "putenv" procedure
  294.  *    so that applications using that procedure will interface
  295.  *    properly to Tcl.  To make it a stand-in, the Makefile will
  296.  *    define "Tcl_PutEnv" to "putenv".
  297.  *
  298.  * Results:
  299.  *    None.
  300.  *
  301.  * Side effects:
  302.  *    The environ array gets updated, as do all of the interpreters
  303.  *    that we manage.
  304.  *
  305.  *----------------------------------------------------------------------
  306.  */
  307.  
  308. int
  309. Tcl_PutEnv(string)
  310.     CONST char *string;        /* Info about environment variable in the
  311.                  * form NAME=value. */
  312. {
  313.     int nameLength;
  314.     char *name, *value;
  315.  
  316.     if (string == NULL) {
  317.     return 0;
  318.     }
  319.  
  320.     /*
  321.      * Separate the string into name and value parts, then call
  322.      * TclSetEnv to do all of the real work.
  323.      */
  324.  
  325.     value = strchr(string, '=');
  326.     if (value == NULL) {
  327.     return 0;
  328.     }
  329.     nameLength = value - string;
  330.     if (nameLength == 0) {
  331.     return 0;
  332.     }
  333.     name = ckalloc((unsigned) nameLength+1);
  334.     memcpy(name, string, (size_t) nameLength);
  335.     name[nameLength] = 0;
  336.     TclSetEnv(name, value+1);
  337.     ckfree(name);
  338.     return 0;
  339. }
  340.  
  341. /*
  342.  *----------------------------------------------------------------------
  343.  *
  344.  * TclUnsetEnv --
  345.  *
  346.  *    Remove an environment variable, updating the "env" arrays
  347.  *    in all interpreters managed by us.  This function is intended
  348.  *    to replace the UNIX "unsetenv" function (but to do this the
  349.  *    Makefile must be modified to redefine "TclUnsetEnv" to
  350.  *    "unsetenv".
  351.  *
  352.  * Results:
  353.  *    None.
  354.  *
  355.  * Side effects:
  356.  *    Interpreters are updated, as is environ.
  357.  *
  358.  *----------------------------------------------------------------------
  359.  */
  360.  
  361. void
  362. TclUnsetEnv(name)
  363.     CONST char *name;            /* Name of variable to remove. */
  364. {
  365.     int index, dummy;
  366.     char **envPtr;
  367.     EnvInterp *eiPtr;
  368.  
  369.     if (environSize == 0) {
  370.     EnvInit();
  371.     }
  372.  
  373.     /*
  374.      * Update the environ array.
  375.      */
  376.  
  377.     index = FindVariable(name, &dummy);
  378.     if (index == -1) {
  379.     return;
  380.     }
  381.     ckfree(environ[index]);
  382.     for (envPtr = environ+index+1; ; envPtr++) {
  383.     envPtr[-1] = *envPtr;
  384.     if (*envPtr == NULL) {
  385.         break;
  386.        }
  387.     }
  388.  
  389.     /*
  390.      * Update all of the interpreters.
  391.      */
  392.  
  393.     for (eiPtr = firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) {
  394.     (void) Tcl_UnsetVar2(eiPtr->interp, "env", (char *) name,
  395.         TCL_GLOBAL_ONLY);
  396.     }
  397. }
  398.  
  399. /*
  400.  *----------------------------------------------------------------------
  401.  *
  402.  * EnvTraceProc --
  403.  *
  404.  *    This procedure is invoked whenever an environment variable
  405.  *    is modified or deleted.  It propagates the change to the
  406.  *    "environ" array and to any other interpreters for whom
  407.  *    we're managing an "env" array.
  408.  *
  409.  * Results:
  410.  *    Always returns NULL to indicate success.
  411.  *
  412.  * Side effects:
  413.  *    Environment variable changes get propagated.  If the whole
  414.  *    "env" array is deleted, then we stop managing things for
  415.  *    this interpreter (usually this happens because the whole
  416.  *    interpreter is being deleted).
  417.  *
  418.  *----------------------------------------------------------------------
  419.  */
  420.  
  421.     /* ARGSUSED */
  422. static char *
  423. EnvTraceProc(clientData, interp, name1, name2, flags)
  424.     ClientData clientData;    /* Not used. */
  425.     Tcl_Interp *interp;        /* Interpreter whose "env" variable is
  426.                  * being modified. */
  427.     char *name1;        /* Better be "env". */
  428.     char *name2;        /* Name of variable being modified, or
  429.                  * NULL if whole array is being deleted. */
  430.     int flags;            /* Indicates what's happening. */
  431. {
  432.     /*
  433.      * First see if the whole "env" variable is being deleted.  If
  434.      * so, just forget about this interpreter.
  435.      */
  436.  
  437.     if (name2 == NULL) {
  438.     register EnvInterp *eiPtr, *prevPtr;
  439.  
  440.     if ((flags & (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED))
  441.         != (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED)) {
  442.         panic("EnvTraceProc called with confusing arguments");
  443.     }
  444.     eiPtr = firstInterpPtr;
  445.     if (eiPtr->interp == interp) {
  446.         firstInterpPtr = eiPtr->nextPtr;
  447.     } else {
  448.         for (prevPtr = eiPtr, eiPtr = eiPtr->nextPtr; ;
  449.             prevPtr = eiPtr, eiPtr = eiPtr->nextPtr) {
  450.         if (eiPtr == NULL) {
  451.             panic("EnvTraceProc couldn't find interpreter");
  452.         }
  453.         if (eiPtr->interp == interp) {
  454.             prevPtr->nextPtr = eiPtr->nextPtr;
  455.             break;
  456.         }
  457.         }
  458.     }
  459.     ckfree((char *) eiPtr);
  460.     return NULL;
  461.     }
  462.  
  463.     /*
  464.      * If a value is being set, call TclSetEnv to do all of the work.
  465.      */
  466.  
  467.     if (flags & TCL_TRACE_WRITES) {
  468.     TclSetEnv(name2, Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY));
  469.     }
  470.  
  471.     if (flags & TCL_TRACE_UNSETS) {
  472.     TclUnsetEnv(name2);
  473.     }
  474.     return NULL;
  475. }
  476.  
  477. /*
  478.  *----------------------------------------------------------------------
  479.  *
  480.  * EnvInit --
  481.  *
  482.  *    This procedure is called to initialize our management
  483.  *    of the environ array.
  484.  *
  485.  * Results:
  486.  *    None.
  487.  *
  488.  * Side effects:
  489.  *    Environ gets copied to malloc-ed storage, so that in
  490.  *    the future we don't have to worry about which entries
  491.  *    are malloc-ed and which are static.
  492.  *
  493.  *----------------------------------------------------------------------
  494.  */
  495.  
  496. static void
  497. EnvInit()
  498. {
  499.     char **newEnviron;
  500.     int i, length;
  501.  
  502.     if (environSize != 0) {
  503.     return;
  504.     }
  505.     for (length = 0; environ[length] != NULL; length++) {
  506.     /* Empty loop body. */
  507.     }
  508.     environSize = length+5;
  509.     newEnviron = (char **) ckalloc((unsigned)
  510.         (environSize * sizeof(char *)));
  511.     for (i = 0; i < length; i++) {
  512.     newEnviron[i] = (char *) ckalloc((unsigned) (strlen(environ[i]) + 1));
  513.     strcpy(newEnviron[i], environ[i]);
  514.     }
  515.     newEnviron[length] = NULL;
  516.     environ = newEnviron;
  517. }
  518.